#ifndef TABLE_H
#define TABLE_H

#include "str.h"
#include "node.h"
#include <string>
#include <vector>
#include <iostream>

using namespace std;

class Table {
public:
    struct Segment {
        string key;
        int type; //0 for int, 1 for char
        int length;
        bool notNull; //0 for false, 1 for true
    };

private:
    string tableName;
    string primaryKey;
    vector<Segment> segments;

public:
    string getName() {
        return tableName;
    }

    string getPrimaryKey() {
        return primaryKey;
    }

    vector<Segment> getSegments() {
        return segments;
    }

    vector<string> getSegmentsString() {
        vector<string> key;
        for(int i = 0; i < segments.size(); i++) {
            key.push_back(segments[i].key);
        }
        return key;
    }

    void setExpression(string expression) {
        //SAMPLE TABLE:
        //CREATE TABLE customer(
        //    id int(10) NOT NULL,
        //    name varchar(25) NOT NULL,
        //    gender varchar(1) NOT NULL,
        //    PRIMARY KEY(id)
        //);
        //expression = "customer(...);"

        segments.clear();
        tableName = Str::removeChar(Str::split(expression, "(")[0], ' ');
        expression = Str::between(expression, "(", ")");

        vector<string> segmentExpressions = Str::split(expression, ",");

        bool existPrimaryKey = false;
        for (int i = 0; i < segmentExpressions.size(); i++) {
            vector<string> words = Str::split(segmentExpressions[i], " ");

            if (words.size() == 2 && words[0] == "PRIMARY" && Str::split(words[1], "(")[0] == "KEY") {
                existPrimaryKey = true;
                primaryKey = Str::between(words[1], "(", ")");
            } else if (words.size() == 2 || (words.size() == 4 && words[2] == "NOT" && words[3] == "NULL")) {
                Segment segment;
                segment.key = words[0];
                if (Str::split(words[1], "(")[0] == "int") {
                    segment.type = 0;
                } else if (Str::split(words[1], "(")[0] == "varchar") {
                    segment.type = 1;
                } else if (Str::split(words[1], "(")[0] == "decimal") {
                    segment.type = 2;
                } else {
                    segment.type = 0;
                    cout << "Not such a variable type in Table::setExpression" << endl;
                }
                segment.length = Str::toNumber(Str::between(words[1], "(", ")"));
                segment.notNull = (words.size() == 4 && words[2] == "NOT" && words[3] == "NULL");
                segments.push_back(segment);
            } else {
                cout << "Syntax error in Table::setExpression" << endl;
            }
        }

        if (!existPrimaryKey) {
            primaryKey = "rid";
            Segment segment;
            segment.key = "rid";
            segment.length = 10;
            segment.notNull = true;
            segments.push_back(segment);
        }
    }

    void setCreateStmt(string _tableName, vector<ColumnDec*> _columnList, PrimaryKey* _primayKey) {
        segments.clear();
        tableName = _tableName;
        if(_primayKey != NULL) {
            primaryKey = _primayKey->columnName;
            delete _primayKey;
        } else {
            //Default primary key is "rid"
            _columnList.push_back(new ColumnDec("rid", new ColumnType(10, "int"), true));
            primaryKey = "rid";
        }
        for(int i = 0; i < _columnList.size(); i++) {
            Segment segment;
            segment.key = _columnList[i]->columnName;

            string type = _columnList[i]->columnType->type;
            if(type == "int") {
                 segment.type = 0;
            } else if (type == "varchar") {
                segment.type = 1;
            } else if (type == "decimal") {
                segment.type = 2;
            } else {
                segment.type = 0;
                cout << "Not such a variable type in Table::setCreateStmt" << endl;
            }
            segment.length = _columnList[i]->columnType->size;
            segment.notNull = _columnList[i]->NotNull;
            segments.push_back(segment);
            delete _columnList[i];
        }
    }

    vector<unsigned int> getData() {
        //TABLE FORM:
        //string tableName;
        //string primaryKey;
        //int segmentNumber;
        //for i in [0 .. segmentNumber - 1]
        //    string key;
        //    int type; //0 for int, 1 for char
        //    int length;
        //    int notNull; //0 for false, 1 for true

        vector<unsigned int> ret;

        ret.push_back(tableName.length());
        for (int i = 0; i < tableName.length(); i++) {
            ret.push_back((unsigned int)tableName[i]);
        }

        ret.push_back(primaryKey.length());
        for (int i = 0; i < primaryKey.length(); i++) {
            ret.push_back((unsigned int)primaryKey[i]);
        }

        ret.push_back(segments.size());

        for (int i = 0; i < segments.size(); i++) {
            ret.push_back(segments[i].key.size());
            for (int j = 0; j < segments[i].key.size(); j++) {
                ret.push_back((unsigned int)segments[i].key[j]);
            }

            ret.push_back(segments[i].type);
            ret.push_back(segments[i].length);
            ret.push_back(segments[i].notNull);
        }

        return ret;
    }

    void setData(vector<unsigned int> data) {
        //TABLE FORM:
        //string tableName;
        //string primaryKey;
        //int segmentNumber;
        //for i in [0 .. segmentNumber - 1]
        //    string key;
        //    int type; //0 for int, 1 for char
        //    int length;
        //    int notNull; //0 for false, 1 for true

        int cnt = 0, strLen = 0;
        strLen = data[cnt++];
        tableName = "";
        for (int i = 0; i < strLen; i++) {
            tableName += (char)data[cnt++];
        }

        strLen = data[cnt++];
        primaryKey = "";
        for (int i = 0; i < strLen; i++) {
            primaryKey += (char)data[cnt++];
        }

        segments.clear();
        int segmentNumber = data[cnt++];
        for (int i = 0; i < segmentNumber; i++) {
            Segment segment;

            strLen = data[cnt++];
            segment.key = "";
            for (int j = 0; j < strLen; j++) {
                segment.key += (char)data[cnt++];
            }

            segment.type = data[cnt++];
            segment.length = data[cnt++];
            segment.notNull = (bool)data[cnt++];

            segments.push_back(segment);
        }
    }

    void output() {
        //OUTPUT FORM:
        //TABLE tableName
        //PRIMARY KEY(primaryKey)
        //for i in [0 .. segmentNumber - 1]
        //    key type(length) (NOT NULL)

        cout << "TABLE " << tableName << endl;
        cout << "    PRIMARY KEY(" << primaryKey << ")" << endl;

        for (int i = 0; i < segments.size(); i++) {
            cout << "    " << segments[i].key << " ";

            if (segments[i].type == 0) {
                cout << "int";
            } else if (segments[i].type == 1) {
                cout << "varchar";
            } else if (segments[i].type == 2) {
                cout << "decimal";
            } else {
                cout << "unknown";
            }

            cout << "(" << segments[i].length << ")";

            if (segments[i].notNull) {
                cout << " NOT NULL";
            }

            cout << endl;
        }
    }

    int getRecordLen() {
        int ret = 0;

        for (int i = 0; i < segments.size(); i++) {
            ret += segments[i].length;
        }

        return ret;
    }
};

#endif
